package cn.lingyangwl.agile.msg.push.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.lingyangwl.agile.auth.*;
import cn.lingyangwl.agile.auth.model.rqrs.*;
import cn.lingyangwl.agile.common.core.exception.enums.*;
import cn.lingyangwl.agile.common.security.utils.*;
import cn.lingyangwl.agile.model.enums.*;
import cn.lingyangwl.agile.model.module.auth.*;
import cn.lingyangwl.agile.msg.push.api.constatnts.*;
import cn.lingyangwl.agile.msg.push.api.model.content.*;
import cn.lingyangwl.agile.msg.push.api.model.rqrs.*;
import cn.lingyangwl.agile.msg.push.api.utils.*;
import cn.lingyangwl.agile.msg.push.model.entity.*;
import cn.lingyangwl.agile.msg.push.service.*;
import cn.lingyangwl.framework.cache.core.service.*;
import cn.lingyangwl.framework.tool.core.*;
import cn.lingyangwl.framework.tool.core.exception.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author shenguangyang
 */
@Service
@Slf4j
public class CaptchaMessageServiceImpl implements CaptchaMessageService {

    @Resource
    private CacheService cacheService;
    @Resource
    private NoticeChannelService noticeChannelService;
    @Resource
    private AuthApi authApi;
    @Resource
    private MsgNoticeService msgNoticeService;


    private MsgParam getMsgContent(SendCaptchaMsgReq req, int code) {
        NoticeCodeEnum.checkExist(req.getTaskCode());
        MsgParam msgParam = new MsgParam();
        msgParam.setCode(req.getTaskCode());
        msgParam.setBizType(req.getBizType());
        msgParam.setToUser(req.getToUser());
        msgParam.addMsgField(MsgFieldEnum.CAPTCHA, code);
        return msgParam;
    }

    @Override
    public void sendAuthUserCaptcha(SendCaptchaMsgReq req) throws Exception {
        // 二次验证图形验证码
        CaptchaCheckReq checkReq = CaptchaCheckReq.builder().imageCaptcha(req.getImageCaptcha()).uuid(req.getUuid()).build();
        if (!authApi.checkCaptcha(checkReq)) {
            throw new BizException("人机验证失败");
        }
        // 删除人机验证码
        authApi.deleteCaptcha(req.getUuid());

        // 对同一个用户发送的消息进行限制
        LoginUser loginUser = SecurityUtils.getLoginUser();
        int code = RandomUtil.randomInt(100000, 999998);

        MsgNotice msgNotice = msgNoticeService.getNoticeByCode(req.getTaskCode());
        if (Objects.isNull(msgNotice)) {
            throw new BizException("不存在任务编码 {}", req.getTaskCode());
        }

        List<NoticeChannel> channelList = noticeChannelService.listChannel(msgNotice.getId(), null);
        NoticeChannel channel = channelList.stream()
                .filter(e -> {
                    ChannelTypeEnum channelType = ChannelTypeEnum.ofByCode(e.getChannelType());
                    String phone = loginUser.getAccount().getPhone();
                    String email = loginUser.getAccount().getEmail();
                    if (channelType == ChannelTypeEnum.SMS) {
                        if (StringUtils.isEmpty(phone)) {
                            throw new BizException("你尚未设置手机号, 请先设置手机号或者使用其他验证方式");
                        }
                        req.setToUser(phone);
                        return RegexUtils.checkPhone(phone);
                    }
                    if (channelType == ChannelTypeEnum.EMAIL) {
                        if (StringUtils.isEmpty(email)) {
                            throw new BizException("你尚未设置邮箱, 请先设置邮箱或者使用其他验证方式");
                        }
                        req.setToUser(email);
                        return RegexUtils.checkEmail(email);
                    }
                    return false;
                }).findFirst().orElse(null);

        MsgParam msgParam = getMsgContent(req, code);
        if (Objects.isNull(channel)) {
            throw new BizException(PushErrorEnum.NO_CONFIG_TEMPLATE);
        }
        ChannelTypeEnum channelType = ChannelTypeEnum.ofByCode(channel.getChannelType());
        String key = String.format(PushCacheCons.CAPTCHA_KEY, req.getToUser(), code);
        if (cacheService.opsForKey().hasKey(key)) {
            throw new BizException("发送过于频繁, 请稍后再试");
        }
        MsgPushUtils.push(channelType, msgParam, req.getToUser());

        // 存储到缓存中
        cacheService.opsForValue().setEx(key, code,60, TimeUnit.SECONDS);
    }

    @Override
    public void sendNoAuthUserCaptcha(SendCaptchaMsgReq req) {
        // 二次验证图形验证码
        CaptchaCheckReq checkReq = CaptchaCheckReq.builder().imageCaptcha(req.getImageCaptcha()).uuid(req.getUuid()).build();
        if (!authApi.checkCaptcha(checkReq)) {
            throw new BizException(AuthErrorEnum.CAPTCHA_ERROR);
        }
        // 删除人机验证码
        authApi.deleteCaptcha(req.getUuid());

        int code = RandomUtil.randomInt(100000, 999999);
        MsgParam msgParam = getMsgContent(req, code);
        MsgNotice msgNotice = msgNoticeService.getNoticeByCode(req.getTaskCode());
        if (Objects.isNull(msgNotice)) {
            throw new BizException("不存在消息编码 {}", req.getTaskCode());
        }

        List<NoticeChannel> channelList = noticeChannelService.listChannel(msgNotice.getId(), null);
        if (CollectionUtils.isEmpty(channelList)) {
            throw new BizException(PushErrorEnum.NO_CONFIG_TEMPLATE);
        }

        if (StringUtils.isEmpty(req.getToUser())) {
            throw new BizException("接收者不能为空");
        }

        NoticeChannel channel = channelList.stream()
                .filter(e -> {
                    ChannelTypeEnum channelType = ChannelTypeEnum.ofByCode(e.getChannelType());
                    if (channelType == ChannelTypeEnum.EMAIL) {
                        return RegexUtils.checkEmail(req.getToUser());
                    }
                    if (channelType == ChannelTypeEnum.SMS) {
                        return RegexUtils.checkPhone(req.getToUser());
                    }
                    return false;
                }).findFirst().orElse(null);
        if (Objects.isNull(channel)) {
            throw new BizException(PushErrorEnum.NO_CONFIG_TEMPLATE);
        }

        ChannelTypeEnum channelType = ChannelTypeEnum.ofByCode(channel.getChannelType());
        // 存储到缓存中
        String key = String.format(PushCacheCons.CAPTCHA_KEY, req.getToUser(), code);
        cacheService.opsForValue().setEx(key, code,60, TimeUnit.SECONDS);
        MsgPushUtils.push(channelType, msgParam, req.getToUser());
    }

    @Override
    public void checkNoAuthUserCaptcha(CheckNoAuthUserCaptchaReq req) {
        NoticeCodeEnum.checkExist(req.getTaskCode());

        String key = String.format(PushCacheCons.CAPTCHA_KEY, req.getToUser(), req.getVerifyCode());
        if (!cacheService.opsForKey().delete(key)) {
            throw new BizException(GlobalErrorEnum.CODE_ERROR);
        }
    }

    @Override
    public void checkAuthUserCaptcha(CheckAuthUserCaptchaReq req) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        MsgNotice msgNotice = msgNoticeService.getNoticeByCode(req.getTaskCode());

        List<NoticeChannel> channelList = noticeChannelService.listChannel(msgNotice.getId(), null);
        if (CollectionUtils.isEmpty(channelList)) {
            throw new BizException(PushErrorEnum.NO_CONFIG_TEMPLATE);
        }
        NoticeChannel channel = channelList.stream()
                .filter(e -> {
                    ChannelTypeEnum channelType = ChannelTypeEnum.ofByCode(e.getChannelType());
                    return channelType == ChannelTypeEnum.EMAIL || channelType == ChannelTypeEnum.SMS;
                }).findFirst().orElse(null);
        if (Objects.isNull(channel)) {
            throw new BizException(PushErrorEnum.NO_CONFIG_TEMPLATE);
        }

        String receiver = "";
        ChannelTypeEnum channelType = ChannelTypeEnum.ofByCode(channel.getChannelType());
        if (channelType == ChannelTypeEnum.SMS) {
            receiver = loginUser.getAccount().getPhone();
        } else if (channelType == ChannelTypeEnum.EMAIL) {
            receiver = loginUser.getAccount().getEmail();
        } else {
            throw new BizException("未处理的渠道类型");
        }

        // 校验验证码
        String key = String.format(PushCacheCons.CAPTCHA_KEY, receiver, req.getVerifyCode());
        if (!cacheService.opsForKey().hasKey(key)) {
            throw new BizException(GlobalErrorEnum.CODE_ERROR);
        }
    }
}
